package restful

import (
	"errors"
	"net/http"

	"github.com/gin-gonic/gin"
)

// 通用的业务错误封装，内部包含错误码、错误详情、解决方式。
type E struct {
	// 业务码
	Code int
	// 错误详情
	Err error
	// 解决方式（可选）
	Solution string
}

// Error 实现了 error 接口，返回错误的具体信息。
// 该方法是 E 类型的一个导出方法，用于外部获取错误详情。
func (e E) Error() string {
	return e.Err.Error()
}

// Is 检查当前错误 E 是否与给定的错误 err 相等。
// 它会比较 e.Err 和 err，或者将 e.Error() 的结果与 err.Error() 进行字符串比较。
// 如果任一比较返回 true，则表示两个错误相等。
func (e E) Is(err error) bool {
	return e.Err == err || e.Error() == err.Error()
}

// GinH 将错误对象 E 转换为 gin.H 类型的映射，用于在 Gin 框架中返回 JSON 格式的错误信息。
// 如果错误对象 E 的 Code 属性大于 0，则将 Code 添加到映射中。
// 适用于导出的函数/API，用于处理错误信息的格式化输出。
func (e E) GinH() gin.H {
	h := gin.H{"msg": e.Error()}
	if e.Code > 0 {
		h["code"] = e.Code
	}
	return h
}

// Restful 方法用于将错误信息以 JSON 格式返回给客户端。
// 该方法接收一个 *gin.Context 类型的参数，表示当前的请求上下文。
// 它通过调用 E 结构体的 GinH 方法获取错误信息的 JSON 表示，
// 并使用 ctx.JSON 方法将其作为响应发送给客户端，状态码为 http.StatusOK。
func (e E) Restful(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, e.GinH())
}

type _errors []E

// FromCode 根据给定的错误码返回对应的错误实例。
// 如果找到匹配的错误码，则返回该错误的指针；否则返回nil。
// 该方法是_errors类型的一个成员方法，用于错误处理。
func (es _errors) FromCode(code int) *E {
	for _, e := range es {
		if e.Code == code {
			return &e
		}
	}
	return nil
}

// FromError 根据传入的错误 error，在错误集合中查找匹配的错误。
// 如果找到匹配的错误，则返回该错误的指针；否则返回 nil。
// 此方法用于错误处理和转换，便于在业务逻辑中统一处理错误类型。
func (es _errors) FromError(err error) *E {
	for _, e := range es {
		if e.Is(err) {
			return &e
		}
	}
	return nil
}

var Errors _errors = make(_errors, 0) // global manager for error that is restful style

// RegisterError 将给定的错误 e 注册到全局错误列表 Errors 中。
// 如果 e.Err 已经存在于 Errors 中，则返回 false，表示注册失败。
// 如果 e.Err 不存在于 Errors 中，则将其添加到 Errors 列表，并返回 true，表示注册成功。
func RegisterError(e E) bool {
	if es := Errors.FromError(e.Err); es != nil {
		return false
	}
	Errors = append(Errors, e)
	return true
}

// RegisterErrorFromCodeAndError 根据给定的错误码和错误对象注册一个新的错误。
// 它创建一个包含指定错误码和错误详情的 E 类型实例，并尝试将其注册。
// 如果注册成功，返回 true；否则返回 false。
func RegisterErrorFromCodeAndError(code int, e error) bool {
	return RegisterError(E{Code: code, Err: e})
}

// RegisterErrorFromCodeAndMessage 根据给定的错误代码和消息字符串注册一个新的错误。
// 它通过调用 RegisterErrorFromCodeAndError 函数并使用 errors.New 创建一个新的错误来实现。
// 参数 code 是错误代码，msg 是描述错误的字符串。
// 返回值为 bool 类型，表示错误是否成功注册。
func RegisterErrorFromCodeAndMessage(code int, msg string) bool {
	return RegisterErrorFromCodeAndError(code, errors.New(msg))
}
